# Connect to Guardian API
library(guardianapi)
gu_api_key()
# Get data include key words "police" OR "law enforcement"
data_all <-gu_content(query='"police" OR "law enforcement"',
from_date="2021-01-01",
to_date="2023-03-31")
# Check the data
dim(data_all)
head(data_all)
# 24863 news articles collected
# Limit the data to only section_name == 'UK news'
data_uk_sec <- subset(data_all, section_name == 'UK news')
dim(data_uk_sec)
head(data_uk_sec)
# Save as CSV file
library(dplyr)
my_data <- data_uk_sec %>%
select(-tags) # remove the non-informative column that contains nested lists
my_df <- as.data.frame(my_data)
write.csv(my_df, "raw_data.csv", row.names = TRUE)
# Read collected data
my_data <- read.csv('raw_data.csv')
dim(my_data)
[1] 3259 47
# Load required packages
library(tidyr)
library(quanteda)
library(caret)
library(dplyr)
library(tidytext)
library(syuzhet)
library(tidyverse)
library(quanteda.textstats)
library(ggplot2)
# Pre-processing
# Select required columns
my_data_hbd <- my_data %>%
select(headline, body_text, first_publication_date)
# Remove duplicates
my_data_hbd <- distinct(my_data_hbd)
# Remove noise
my_data_hbd$body_text <- gsub("\\W+", " ", my_data_hbd$body_text)
# Tokenise the data and remove punctuation, numbers, and symbols
# Convert all text to lowercase and apply stemming
my_data_tokens <- tokens(my_data_hbd$body_text, remove_punct = TRUE, remove_numbers = TRUE, remove_symbols = TRUE) %>% tokens_remove(stopwords("english")) %>% tokens_tolower() %>% tokens_wordstem(language = quanteda_options("language_stemmer"))
dim(my_data_hbd)# 3194 UK news articles after pre-processing
[1] 3194 3
head(my_data_hbd)
## Bing lexicon
# Try to use "bing" lexicon to get sentiment score for each news article
# Convert the data to a tidy format
tokens <- my_data_hbd %>%
unnest_tokens(word, body_text) %>%
anti_join(stop_words)
Joining with `by = join_by(word)`
# Load the lexicon
lexicon <- get_sentiments("bing")
# Join the lexicon with data
my_data_sentiment <- tokens %>%
inner_join(lexicon)
Joining with `by = join_by(word)`
# Calculate the sentiment score
my_data_sentiment_scores <- my_data_sentiment %>%
count(headline, sentiment) %>%
pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>%
mutate(sentiment_score = positive - negative)
# Visualise the sentiment scores
ggplot(my_data_sentiment_scores, aes(x = sentiment_score)) +
geom_histogram(binwidth = 2, color = "black", fill = "lightblue") +
labs(title = "Distribution of Sentiment Scores",
x = "Sentiment Score",
y = "Frequency") +
theme_minimal()

# Categorise sentiment scores as positive, negative, or neutral
my_data_sentiment_category <- my_data_sentiment_scores %>%
mutate(sentiment_category = ifelse(sentiment_score > 0, "Positive",
ifelse(sentiment_score < 0, "Negative", "Neutral")))
# Count the number of articles in each sentiment category
my_data_sentiment_counts <- my_data_sentiment_category %>%
count(sentiment_category)
print(my_data_sentiment_counts)
# Visualise the distribution of sentiment categories in articles
ggplot(data = my_data_sentiment_counts, aes(x = sentiment_category, y = n, fill = sentiment_category)) +
geom_col() +
geom_text(aes(label = n), position = position_stack(vjust = 0.5), color = "black", size = 4) +
labs(x = "Sentiment Category", y = "Number of Articles", fill = "Sentiment Category") +
ggtitle("Sentiment Categories Proportions Based on Bing Lexicon")

# Try to explore sentiment change over time based on Bing lexicon
# Calculate the sentiment score by date
sentiment_scores_date <- my_data_sentiment %>%
count(first_publication_date, sentiment) %>%
pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>%
mutate(sentiment_score = positive - negative)
# Categorise sentiment scores as positive, negative, or neutral
sentiment_category_date <- sentiment_scores_date %>%
mutate(sentiment_category = ifelse(sentiment_score > 0, "Positive",
ifelse(sentiment_score < 0, "Negative", "Neutral")))
# Convert date to date object
sentiment_category_date$first_publication_date <- as.Date(sentiment_category_date$first_publication_date)
# Visualise the sentiment scores over time
ggplot(sentiment_category_date, aes(x = first_publication_date, y = sentiment_score)) +
geom_line(color = "#000080") +
geom_hline(yintercept = 0, color = "red") +
labs(title = "Sentiment Score Over Time Based on Bing Lexicon",
x = "Date",
y = "Sentiment Score") +
theme_minimal()

## Use get_sentiment() from syuzhet package to get sentiment score for each news article
library(syuzhet)
# Calculate the sentiment scores
my_data_hbd$sentiment_score <- get_sentiment(my_data_hbd$body_text)
# Categorise sentiment scores as positive, negative, or neutral
my_data_hbd$sentiment_category <- cut(my_data_hbd$sentiment_score, breaks=c(-Inf,-0.1,0.1,Inf), labels=c("Negative", "Neutral", "Positive"))
# Visualise the distribution of sentiment scores
ggplot(my_data_hbd, aes(x=sentiment_score, fill=sentiment_category)) +
geom_histogram(binwidth=0.05, alpha=0.5, position="identity") +
labs(title="Sentiment Scores Distribution", x="Sentiment Score", y="Count") +
theme_minimal()

# Visualise the proportion of positive, negative, and neutral sentiment categories
ggplot(my_data_hbd, aes(x = sentiment_category, fill = sentiment_category)) +
geom_bar() +
geom_text(stat = "count", aes(label = after_stat(count)),
position = position_stack(vjust = 0.5)) +
labs(title = "Sentiment Categories Proportions Based on Syuzhet Package",
x = "Sentiment Category", y = "Number of Articles") +
theme_minimal() +
theme(panel.background = element_rect(fill = "grey90", color = NA),
panel.grid.major = element_line(color = "white"),
panel.grid.minor = element_blank())

# Try to explore sentiment change over time based on syuzhet package
# Convert first_publication_date to a date object
my_data_hbd$first_publication_date <- as.Date(my_data_hbd$first_publication_date)
# Aggregate sentiment score by date
sentiment_by_date <- my_data_hbd %>%
group_by(first_publication_date) %>%
summarise(sentiment_score = mean(sentiment_score))
# Create a line chart of sentiment score over time
ggplot(sentiment_by_date, aes(x=first_publication_date, y=sentiment_score)) +
geom_line(color = "#000080") +
geom_hline(yintercept = 0, color = "red") +
labs(title="Sentiment Score over Time Based on Syuzhet Package", x="Date", y="Sentiment Score") +
theme_minimal()

# Plot the sentiment score over time together (Bing Lexicon and Syuzhet Package)
library(patchwork)
library(scales)
# Create the first plot
plota <- ggplot(sentiment_category_date, aes(x = first_publication_date, y = sentiment_score)) +
geom_line(color = "#000080") +
geom_hline(yintercept = 0, color = "red") +
labs(title = "Sentiment Score Over Time Based on Bing Lexicon",
x = "Date",
y = "Sentiment Score") +
theme_minimal() +
scale_x_date(date_breaks = "2 months", date_labels = "%b %y")
# Create the second plot
plotb <- ggplot(sentiment_by_date, aes(x=first_publication_date, y=sentiment_score)) +
geom_line(color = "#000080") +
geom_hline(yintercept = 0, color = "red") +
labs(title="Sentiment Score over Time Based on Syuzhet Package", x="Date", y="Sentiment Score") +
theme_minimal() +
scale_x_date(date_breaks = "2 months", date_labels = "%b %y")
# Combine the plots vertically using patchwork
plota / plotb

## Supervised Machine Learning
# Try to apply machine learning technique, build a model to predict sentiment
# Map the values of the original column "sentiment_category" to integers
my_data_ml <- my_data_hbd %>%
mutate(id = row_number(), # add a new column with row numbers
sentiment_category = case_when(
sentiment_category == "Positive" ~ 2,
sentiment_category == "Negative" ~ 1,
sentiment_category == "Neutral" ~ 0,
TRUE ~ NA_real_ # to handle any other cases
))
# Extract unigrams, bigrams as features
unigrams <- my_data_ml$body_text %>%
tokens(remove_punct = T) %>%
tokens_ngrams(1) %>%
dfm()
bigrams <- my_data_ml$body_text %>%
tokens(remove_punct = T) %>%
tokens_ngrams(2) %>%
dfm()
# Apply sparsity correction to reduce the zero counts
unigrams_corrected <- dfm_trim(unigrams, sparsity = .90)
bigrams_corrected <- dfm_trim(bigrams, sparsity = .90)
# Check dimensions
dim(unigrams_corrected)
[1] 3194 622
# TFIDF-weighted ngrams
unigrams_tfidf <- dfm_tfidf(unigrams_corrected,
scheme_tf = 'count',
scheme_df = 'inverse')
bigrams_tfidf <- dfm_tfidf(bigrams_corrected,
scheme_tf = 'count',
scheme_df = 'inverse')
# Convert ngrams to data frames
news_unigrams <- convert(unigrams_tfidf, 'data.frame')
news_bigrams <- convert(bigrams_tfidf, 'data.frame')
# Add id column to ngrams to join unigrams and bigrams later
news_unigrams$id <- my_data_ml$id
news_bigrams$id <- my_data_ml$id
# Remove doc_id column
unigrams_tfidf_df <- subset(news_unigrams, select = -c(doc_id))
bigrams_tfidf_df <- subset(news_bigrams, select = -c(doc_id))
# Join the two datasets
ml_data <- bigrams_tfidf_df %>% left_join(unigrams_tfidf_df, by = "id")
# Get sentiment scores as features
ml_data$sentiment_score <- get_sentiment(my_data_ml$body_text)
# Add label column
ml_data$label <- my_data_ml$sentiment_category
# Remove the id column
ml_data <- ml_data %>% select(-id)
# Map labels
ml_data$label <- ifelse(ml_data$label == "2", "Positive",
ifelse(ml_data$label == "1", "Negative", "Neutral"))
# Convert label to factor
ml_data$label <- as.factor(ml_data$label) # convert label to factor
# Apply machine learning setting
library(caret)
set.seed(123)
for_training <- createDataPartition(y = ml_data$label,
p = .8,
list = FALSE)
training_data <- ml_data[for_training,]
testing_data <- ml_data[-for_training,]
# Set training control parameters
training_controls <- trainControl(method="cv",
number = 5,
classProbs = T)
## Train the SVM model
model.svm <- train(label ~ .,
data = training_data,
method = "svmLinear",
trControl = training_controls,
preProcess = c('zv')
)
# Predict on the testing data
pred_svm <- predict(model.svm, testing_data)
# Evaluate the model
confusionMatrix(pred_svm, as.factor(testing_data$label), mode="prec_recall")
Confusion Matrix and Statistics
Reference
Prediction Negative Neutral Positive
Negative 431 5 42
Neutral 0 0 0
Positive 21 1 137
Overall Statistics
Accuracy : 0.8917
95% CI : (0.8649, 0.9147)
No Information Rate : 0.7096
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.7274
Mcnemar's Test P-Value : 0.004637
Statistics by Class:
Class: Negative Class: Neutral Class: Positive
Precision 0.9017 NA 0.8616
Recall 0.9535 0.000000 0.7654
F1 0.9269 NA 0.8107
Prevalence 0.7096 0.009419 0.2810
Detection Rate 0.6766 0.000000 0.2151
Detection Prevalence 0.7504 0.000000 0.2496
Balanced Accuracy 0.8497 0.500000 0.8587
## Train the Naive Bayes model
model.nb <- train(label ~ .,
data = training_data,
method = "naive_bayes",
trControl = training_controls,
preProcess = c('zv'))
# Predict on the testing data
pred_nb <- predict(model.nb, testing_data)
# Evaluate the model
confusionMatrix(pred_nb, as.factor(testing_data$label), mode="prec_recall")
Confusion Matrix and Statistics
Reference
Prediction Negative Neutral Positive
Negative 451 6 174
Neutral 1 0 5
Positive 0 0 0
Overall Statistics
Accuracy : 0.708
95% CI : (0.671, 0.7431)
No Information Rate : 0.7096
P-Value [Acc > NIR] : 0.5544
Kappa : 0.0169
Mcnemar's Test P-Value : <2e-16
Statistics by Class:
Class: Negative Class: Neutral Class: Positive
Precision 0.7147 0.000000 NA
Recall 0.9978 0.000000 0.000
F1 0.8329 NaN NA
Prevalence 0.7096 0.009419 0.281
Detection Rate 0.7080 0.000000 0.000
Detection Prevalence 0.9906 0.009419 0.000
Balanced Accuracy 0.5124 0.495246 0.500
# Set training control parameters
training_control <- trainControl(method = "cv",
number = 5,
verboseIter = FALSE)
# Train the Neural Network (NN) model
model.nn <- train(label ~ .,
data = training_data,
trControl = training_control,
method = "nnet",
MaxNWts = 10000,
maxit = 10)
# weights: 937
initial value 3012.317783
iter 10 value 215.740063
final value 215.740063
stopped after 10 iterations
# weights: 2805
initial value 2890.927954
iter 10 value 528.905183
final value 528.905183
stopped after 10 iterations
# weights: 4673
initial value 1631.311019
iter 10 value 81.493961
final value 81.493961
stopped after 10 iterations
# weights: 937
initial value 2205.419836
iter 10 value 375.374810
final value 375.374810
stopped after 10 iterations
# weights: 2805
initial value 4080.726308
iter 10 value 235.414024
final value 235.414024
stopped after 10 iterations
# weights: 4673
initial value 2097.277411
iter 10 value 441.662316
final value 441.662316
stopped after 10 iterations
# weights: 937
initial value 1797.509188
iter 10 value 454.281650
final value 454.281650
stopped after 10 iterations
# weights: 2805
initial value 1522.231448
iter 10 value 76.772175
final value 76.772175
stopped after 10 iterations
# weights: 4673
initial value 2790.914079
iter 10 value 149.951239
final value 149.951239
stopped after 10 iterations
# weights: 937
initial value 2703.766546
iter 10 value 231.052442
final value 231.052442
stopped after 10 iterations
# weights: 2805
initial value 4350.443913
iter 10 value 175.537187
final value 175.537187
stopped after 10 iterations
# weights: 4673
initial value 1915.247997
iter 10 value 67.946871
final value 67.946871
stopped after 10 iterations
# weights: 937
initial value 2225.431471
iter 10 value 371.623557
final value 371.623557
stopped after 10 iterations
# weights: 2805
initial value 3346.243383
iter 10 value 530.165846
final value 530.165846
stopped after 10 iterations
# weights: 4673
initial value 2232.140065
iter 10 value 860.015426
final value 860.015426
stopped after 10 iterations
# weights: 937
initial value 2453.714672
iter 10 value 459.896268
final value 459.896268
stopped after 10 iterations
# weights: 2805
initial value 2948.106984
iter 10 value 108.793768
final value 108.793768
stopped after 10 iterations
# weights: 4673
initial value 3258.825327
iter 10 value 93.066605
final value 93.066605
stopped after 10 iterations
# weights: 937
initial value 1687.055584
iter 10 value 175.437276
final value 175.437276
stopped after 10 iterations
# weights: 2805
initial value 2275.372509
iter 10 value 501.559418
final value 501.559418
stopped after 10 iterations
# weights: 4673
initial value 2893.730150
iter 10 value 83.585677
final value 83.585677
stopped after 10 iterations
# weights: 937
initial value 2687.798446
iter 10 value 280.086403
final value 280.086403
stopped after 10 iterations
# weights: 2805
initial value 2445.555162
iter 10 value 162.961999
final value 162.961999
stopped after 10 iterations
# weights: 4673
initial value 2875.294472
iter 10 value 172.413339
final value 172.413339
stopped after 10 iterations
# weights: 937
initial value 2650.707482
iter 10 value 549.678533
final value 549.678533
stopped after 10 iterations
# weights: 2805
initial value 2829.549362
iter 10 value 200.365957
final value 200.365957
stopped after 10 iterations
# weights: 4673
initial value 2421.146339
iter 10 value 79.565507
final value 79.565507
stopped after 10 iterations
# weights: 937
initial value 2015.110133
iter 10 value 184.705076
final value 184.705076
stopped after 10 iterations
# weights: 2805
initial value 2169.741973
iter 10 value 194.031525
final value 194.031525
stopped after 10 iterations
# weights: 4673
initial value 2027.832382
iter 10 value 84.826725
final value 84.826725
stopped after 10 iterations
# weights: 937
initial value 2035.805987
iter 10 value 361.348988
final value 361.348988
stopped after 10 iterations
# weights: 2805
initial value 3230.270754
iter 10 value 281.020927
final value 281.020927
stopped after 10 iterations
# weights: 4673
initial value 1598.373160
iter 10 value 258.396820
final value 258.396820
stopped after 10 iterations
# weights: 937
initial value 1867.947297
iter 10 value 235.219160
final value 235.219160
stopped after 10 iterations
# weights: 2805
initial value 3175.434933
iter 10 value 115.798166
final value 115.798166
stopped after 10 iterations
# weights: 4673
initial value 3139.111259
iter 10 value 125.734630
final value 125.734630
stopped after 10 iterations
# weights: 937
initial value 1794.762865
iter 10 value 402.371864
final value 402.371864
stopped after 10 iterations
# weights: 2805
initial value 2308.690976
iter 10 value 127.198993
final value 127.198993
stopped after 10 iterations
# weights: 4673
initial value 3587.218789
iter 10 value 297.734430
final value 297.734430
stopped after 10 iterations
# weights: 937
initial value 2855.653728
iter 10 value 439.341461
final value 439.341461
stopped after 10 iterations
# weights: 2805
initial value 3714.714161
iter 10 value 227.465379
final value 227.465379
stopped after 10 iterations
# weights: 4673
initial value 2852.131228
iter 10 value 134.047050
final value 134.047050
stopped after 10 iterations
# weights: 937
initial value 2740.447362
iter 10 value 882.667978
final value 882.667978
stopped after 10 iterations
# weights: 2805
initial value 3239.106458
iter 10 value 201.950394
final value 201.950394
stopped after 10 iterations
# weights: 4673
initial value 2086.209728
iter 10 value 61.603887
final value 61.603887
stopped after 10 iterations
# weights: 2805
initial value 2785.648012
iter 10 value 577.050423
final value 577.050423
stopped after 10 iterations
# Predict on the testing data
pred_nn <- predict(model.nn, testing_data)
# Evaluate the model
confusionMatrix(pred_nn, testing_data$label, mode = "prec_recall")
Confusion Matrix and Statistics
Reference
Prediction Negative Neutral Positive
Negative 447 1 2
Neutral 0 0 0
Positive 5 5 177
Overall Statistics
Accuracy : 0.9796
95% CI : (0.9654, 0.9891)
No Information Rate : 0.7096
P-Value [Acc > NIR] : < 2e-16
Kappa : 0.951
Mcnemar's Test P-Value : 0.06333
Statistics by Class:
Class: Negative Class: Neutral Class: Positive
Precision 0.9933 NA 0.9465
Recall 0.9889 0.000000 0.9888
F1 0.9911 NA 0.9672
Prevalence 0.7096 0.009419 0.2810
Detection Rate 0.7017 0.000000 0.2779
Detection Prevalence 0.7064 0.000000 0.2936
Balanced Accuracy 0.9864 0.500000 0.9835
## Unsupervised Machine Learning
# Try to apply k-means clustering model using the extracted features
# Use ngrams frequencies as features
ml_grams <- bigrams_tfidf_df %>% left_join(unigrams_tfidf_df, by = "id")
# Remove the id column
ml_grams <- ml_grams %>% select(-id)
# Use the k-means model on n-grams
# Determine k
wss <- numeric()
for(i in 1:10){
ngrams_kmeans_temp <- kmeans(x = ml_grams,
centers = i,
iter.max = 20,
nstart = 10)
wss[i] <- ngrams_kmeans_temp$tot.withinss
}
{plot(wss, type='b')}

# Settle for k=2
ngrams_kmeans <- kmeans(ml_grams,
centers = 2,
iter.max = 10,
nstart = 10)
# Look at the clusters by their most prominent ngram differences
# Cluster 1
cat("Cluster 1: \n")
Cluster 1:
print(head(sort(ngrams_kmeans$centers[1, ], decreasing = TRUE), n = 20))
royal her state will will_be
53.78888 31.04378 29.11604 23.43286 22.49379
prime prime_minister minister she here
22.42150 19.98957 19.88383 18.28677 15.88477
service world members_of wales new
15.34549 14.74088 13.74756 13.56348 13.49116
death i country today members
13.31633 13.06975 12.94854 12.84766 12.54317
# Cluster 2
cat("\nCluster 2: \n")
Cluster 2:
print(head(sort(ngrams_kmeans$centers[2, ], decreasing = TRUE), n = 20))
her she i his met he the_met
0.8976625 0.8137124 0.6721067 0.5938533 0.5798295 0.5744155 0.5516751
women you officers him court people we
0.5282233 0.4786335 0.4515644 0.4432352 0.4309058 0.4273825 0.4235406
family t government he_was uk london
0.4209670 0.4143618 0.4004978 0.3901206 0.3864412 0.3794096
# Use the k-means model on sentiment score
# Determine k
wss <- numeric()
for(i in 1:10){
sentiment_kmeans_temp <- kmeans(x = ml_data$sentiment_score,
centers = i,
iter.max = 10,
nstart = 10)
wss[i] <- sentiment_kmeans_temp$tot.withinss
}
{plot(wss, type='b')}

# Settle for k=3
sentiment_kmeans <- kmeans(ml_data$sentiment_score,
centers = 3,
iter.max = 5,
nstart = 5)
# Print the centers of each sentiment cluster
print(sentiment_kmeans$centers[1, ])
1
1.58212
print(sentiment_kmeans$centers[2, ])
2
-10.96622
print(sentiment_kmeans$centers[3, ])
3
99.87857
# Visualise the clusters
# Add the cluster labels to the original data frame
ml_data$cluster <- factor(sentiment_kmeans$cluster)
# Create the boxplot
ggplot(ml_data, aes(x = cluster, y = sentiment_score)) +
geom_boxplot() +
xlab("Cluster") +
ylab("Sentiment Score")

### Topic Modelling
# Load required packages
library(tidyr)
library(quanteda)
library(caret)
library(dplyr)
library(tidytext)
library(topicmodels)
library(ldatuning)
library(stm)
library(wordcloud)
library(tm)
library(ggplot2)
# Prepare the data and select required columns
my_data_hbd <- my_data %>%
select(headline, body_text, first_publication_date)
# Remove duplicates
my_data_hbd <- distinct(my_data_hbd)
# Remove any noise in the data
my_data_hbd$body_text <- gsub("\\W+", " ", my_data_hbd$body_text)
# Tokenise the data and remove punctuation, numbers, and symbols
# Convert all text to lowercase, apply stemming, and remove non-informative words for topic modelling purposes
tm_tokens <- tokens(my_data_hbd$body_text, remove_punct = TRUE, remove_numbers = TRUE, remove_symbols = TRUE) %>% tokens_remove(stopwords("english")) %>% tokens_tolower() %>% tokens_wordstem(language = quanteda_options("language_stemmer")) %>% tokens_remove(c("polic", "offic", "peopl", "year", "day", "will", "also", "t", "s", "can", "uk", "british"))
# Create a Document Feature Matrix (dfm)
dfm_tm <- dfm(tm_tokens)
# Trim the dfm for frequently and rarely occurring terms
dfm.trim <- dfm_trim(dfm_tm, min_docfreq = 0.075, max_docfreq = 0.9, docfreq_type = "prop")
dfm.trim
Document-feature matrix of: 3,194 documents, 764 features (83.44% sparse) and 0 docvars.
features
docs immedi govern quit human right issu stop small cross across
text1 2 1 1 6 8 1 1 1 2 1
text2 0 3 0 0 0 1 1 0 0 2
text3 0 4 1 2 1 0 1 0 0 0
text4 0 2 0 0 0 1 0 3 1 2
text5 0 1 0 0 1 2 0 0 0 0
text6 0 0 0 0 0 0 11 0 0 2
[ reached max_ndoc ... 3,188 more documents, reached max_nfeat ... 754 more features ]
## LDA topic modelling
n.topics <- 5
dfm2topicmodels <- convert(dfm.trim, to = "topicmodels")
lda.model <- LDA(dfm2topicmodels, n.topics, control = list(seed=111))
lda.model
A LDA_VEM topic model with 5 topics.
# Extract the topic-term matrix (beta) from the LDA model
beta_topics <- tidy(lda.model, matrix = "beta")
beta_topics
# Group the terms by topics
beta_top_terms <- beta_topics %>%
group_by(topic) %>%
slice_max(beta, n = 10) %>%
ungroup() %>%
arrange(topic, -beta)
# Display the group terms
beta_top_terms %>%
mutate(term = reorder_within(term, beta, topic)) %>%
ggplot(aes(beta, term, fill = factor(topic))) +
geom_col(show.legend = FALSE) +
facet_wrap(~ topic, scales = "free") +
scale_y_reordered()

# Top 10 terms for each topics
as.data.frame(terms(lda.model, 10))
# List the topic number for each news article in the corpus.
data.frame(Topic = topics(lda.model))
# Convert the topic model to a data frame
topics_df <- data.frame(topics(lda.model))
# Plot the count of topics
ggplot(topics_df, aes(x = factor(topics(lda.model)))) +
geom_bar(aes(fill = factor(topics(lda.model))), alpha = 0.8) +
scale_fill_discrete(name = "Topic") +
labs(title = "Topic Distribution Based on Latent Dirichlet Allocation (LDA)", x = "Topic", y = "Count") +
theme(plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1),
axis.title = element_text(size = 12),
panel.background = element_rect(fill = "white"),
panel.grid.major = element_line(colour = "gray90")) +
geom_text(stat='count', aes(label=..count..), vjust=-0.5)

## Structural Topic Modeling (STM)
n.topics <- 5
dfm2stm <- convert(dfm.trim, to = "stm")
modell.stm <- stm(dfm2stm$documents, dfm2stm$vocab, K = n.topics, data = dfm2stm$meta, init.type = "Spectral")
Beginning Spectral Initialization
Calculating the gram matrix...
Finding anchor words...
.....
Recovering initialization...
.......
Initialization complete.
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 1 (approx. per word bound = -6.371)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 2 (approx. per word bound = -6.316, relative change = 8.618e-03)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 3 (approx. per word bound = -6.296, relative change = 3.166e-03)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 4 (approx. per word bound = -6.288, relative change = 1.287e-03)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 5 (approx. per word bound = -6.284, relative change = 6.390e-04)
Topic 1: court, investig, case, alleg, told
Topic 2: incid, fire, investig, area, servic
Topic 3: govern, met, home, protest, forc
Topic 4: famili, old, murder, told, attack
Topic 5: royal, time, minist, state, famili
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 6 (approx. per word bound = -6.282, relative change = 3.647e-04)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 7 (approx. per word bound = -6.280, relative change = 2.298e-04)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 8 (approx. per word bound = -6.279, relative change = 1.553e-04)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 9 (approx. per word bound = -6.279, relative change = 1.102e-04)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 10 (approx. per word bound = -6.278, relative change = 8.075e-05)
Topic 1: court, investig, case, alleg, told
Topic 2: incid, investig, fire, area, man
Topic 3: met, govern, home, forc, protest
Topic 4: famili, old, told, murder, friend
Topic 5: royal, state, time, minist, servic
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 11 (approx. per word bound = -6.278, relative change = 6.053e-05)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 12 (approx. per word bound = -6.278, relative change = 4.604e-05)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 13 (approx. per word bound = -6.277, relative change = 3.556e-05)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 14 (approx. per word bound = -6.277, relative change = 2.763e-05)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 15 (approx. per word bound = -6.277, relative change = 2.159e-05)
Topic 1: court, investig, case, alleg, charg
Topic 2: incid, investig, fire, area, arrest
Topic 3: met, govern, home, forc, protest
Topic 4: famili, told, old, murder, say
Topic 5: royal, state, minist, time, servic
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 16 (approx. per word bound = -6.277, relative change = 1.699e-05)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 17 (approx. per word bound = -6.277, relative change = 1.340e-05)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Completing Iteration 18 (approx. per word bound = -6.277, relative change = 1.054e-05)
.......................................................................................................
Completed E-Step (1 seconds).
Completed M-Step.
Model Converged
# Top 10 terms for each topic
as.data.frame(t(labelTopics(modell.stm, n = 10)$prob))
# Generate a word cloud for the first topic
par(mar=c(0.5, 0.5, 0.5, 0.5))
cloud(modell.stm, topic = 1, scale = c(2.25,.5))

# Plot a summary of the estimated topic shares for the corpus
plot(modell.stm, type = "summary", text.cex = 0.5, main = "Topic shares on the corpus as a whole", xlab = "estimated share of topics")

# Print the top 5 terms for each of the 5 topics
labelTopics(modell.stm,topics = c(1:5), n=10)
Topic 1 Top Words:
Highest Prob: court, investig, case, alleg, charg, evid, told, inquiri, victim, sexual
FREX: alleg, court, sexual, prosecut, charg, evid, rape, case, sentenc, offenc
Lift: rape, prosecutor, prosecut, sexual, judg, alleg, convict, jail, sentenc, court
Score: rape, court, sexual, investig, alleg, prosecut, guilti, juri, sentenc, convict
Topic 2 Top Words:
Highest Prob: incid, investig, arrest, area, man, fire, london, servic, report, two
FREX: incid, fire, area, road, scene, arrest, hospit, manchest, west, search
Lift: fire, condit, vehicl, incid, road, suspicion, locat, scene, area, injur
Score: fire, incid, investig, road, area, hospit, man, search, scene, resid
Topic 3 Top Words:
Highest Prob: met, govern, home, forc, protest, women, public, report, say, crime
FREX: protest, commission, govern, secretari, organis, chief, violenc, labour, right, crime
Lift: protest, patel, commission, anti, mayor, tackl, institut, improv, polici, cultur
Score: protest, govern, commission, secretari, crime, patel, everard, met, minist, women
Topic 4 Top Words:
Highest Prob: famili, told, old, say, murder, one, go, friend, time, just
FREX: friend, mother, boy, kill, love, know, son, school, dog, girl
Lift: dog, daughter, teenag, parent, didn, friend, sister, boy, son, father
Score: dog, juri, mother, murder, famili, boy, stab, friend, son, love
Topic 5 Top Words:
Highest Prob: royal, state, minist, time, servic, famili, new, one, first, countri
FREX: royal, state, ireland, northern, minist, prime, countri, pay, respect, world
Lift: ireland, royal, northern, prime, pay, respect, state, world, visit, great
Score: ireland, royal, minist, northern, prime, tribut, state, leader, parliament, countri
# Plot histograms of the estimated topic shares within the documents for 3 randomly selected topics
plot(modell.stm, type = "hist", topics = sample(1:n.topics, size = 3), main = "histogram of the topic shares within the documents")

# Plot the top terms for each of the 5 topics
plot(modell.stm, type = "labels", topics = c(1,2,3,4,5), main = "Topic terms")

library(ggplot2)
library(dplyr)
# Identify the most probable topic for each document
top_topics <- apply(modell.stm$theta, 1, which.max)
# Add the topic labels to the original data frame
my_data_hbd$Topic_stm <- top_topics
# Calculate the count of each topic
df_topic_counts <- my_data_hbd %>%
group_by(Topic_stm) %>%
summarize(count = n())
# Create the plot
ggplot(df_topic_counts, aes(x = factor(Topic_stm), y = count, fill = factor(Topic_stm))) +
geom_bar(stat = "identity", alpha = 0.8) +
scale_fill_discrete(name = "Topic") +
labs(title = "Topic Distribution Based on Structural Topic Modeling (STM)", x = "Topic", y = "Count") +
theme(plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1),
axis.title = element_text(size = 12),
panel.background = element_rect(fill = "white"),
panel.grid.major = element_line(colour = "gray90")) +
geom_text(aes(label = count), vjust=-0.5)

## To see how the topics changed over time (LDA model)
# Add the topic labels to the original data frame
my_data_hbd$Topic_lda <- topics(lda.model)
# Convert the date column to a Date object
my_data_hbd$first_publication_date <- as.Date(my_data_hbd$first_publication_date, format = "%Y-%m-%d")
# Calculate the proportion of each topic by year
topic_proportions <- my_data_hbd %>%
group_by(Topic_lda, year = lubridate::year(first_publication_date)) %>%
summarise(count = n(), .groups = 'drop') %>%
ungroup() %>%
group_by(year) %>%
mutate(proportion = count / sum(count))
# Plot the topic proportions over time
ggplot(topic_proportions, aes(x = year, y = proportion, color = factor(Topic_lda))) +
geom_line() +
labs(x = "Year", y = "Proportion", color = "Topic") +
theme_bw()

# Calculate the proportion of each topic by half-year
topic_proportions_half_year <- my_data_hbd %>%
group_by(Topic_lda, year = lubridate::year(first_publication_date), half_year = lubridate::floor_date(first_publication_date, unit = "6 months")) %>%
summarise(count = n(), .groups = 'drop') %>%
ungroup() %>%
group_by(year, half_year) %>%
mutate(proportion = count / sum(count))
# Plot the topic proportions over time
ggplot(topic_proportions_half_year, aes(x = half_year, y = proportion, color = factor(Topic_lda))) +
geom_line() +
labs(x = "Half-Year", y = "Proportion", color = "Topic") +
theme_bw()

## To see how the topics changed over time (STM model)
# Identify the most probable topic for each document
top_topics <- apply(modell.stm$theta, 1, which.max)
# Add the topic labels to the original data frame
my_data_hbd$Topic_stm <- top_topics
# Calculate the proportion of each topic by year
topic_proportions_stm <- my_data_hbd %>%
group_by(Topic_stm, year = lubridate::year(first_publication_date)) %>%
summarise(count = n(), .groups = 'drop') %>%
ungroup() %>%
group_by(year) %>%
mutate(proportion = count / sum(count))
# Plot the topic proportions over time
ggplot(topic_proportions_stm, aes(x = year, y = proportion, color = factor(Topic_stm))) +
geom_line() +
labs(x = "Year", y = "Proportion", color = "Topic") +
theme_bw()

# Calculate the proportion of each topic by half-year
topic_proportions_stm_half_year <- my_data_hbd %>%
group_by(Topic_stm, year = lubridate::year(first_publication_date), half_year = lubridate::floor_date(first_publication_date, unit = "6 months")) %>%
summarise(count = n(), .groups = 'drop') %>%
ungroup() %>%
group_by(year, half_year) %>%
mutate(proportion = count / sum(count))
# Plot the topic proportions over time
ggplot(topic_proportions_stm_half_year, aes(x = half_year, y = proportion, color = factor(Topic_stm))) +
geom_line() +
labs(x = "Half-Year", y = "Proportion", color = "Topic") +
theme_bw()

# Refine the graphs, add the top 5 terms for each topic to the legend (LDA model)
# Extract the top 5 terms for each topic
top_terms <- terms(lda.model, 5)
# Create label for color legend
legend_label <- paste("Top 5 Terms\n",
"1: ", paste(top_terms[,1], collapse = ", "), "\n",
"2: ", paste(top_terms[,2], collapse = ", "), "\n",
"3: ", paste(top_terms[,3], collapse = ", "), "\n",
"4: ", paste(top_terms[,4], collapse = ", "), "\n",
"5: ", paste(top_terms[,5], collapse = ", "), sep = "")
# Plot the topic proportions over time
ggplot(topic_proportions_half_year, aes(x = half_year, y = proportion, color = factor(Topic_lda))) +
geom_line() +
labs(x = "Half-Year", y = "Proportion",
color = legend_label,
subtitle = "Topic Changes Over Time Using LDA Topic Modelling") +
theme_bw()

# Refine the graphs, add the top 5 terms for each topic to the legend (STM model)
# Extract the top 5 terms for each topic
top_terms_stm <- labelTopics(modell.stm, topics = c(1:5), n=5)
top_terms_stm$frex
[,1] [,2] [,3] [,4] [,5]
[1,] "alleg" "court" "sexual" "prosecut" "charg"
[2,] "incid" "fire" "area" "road" "scene"
[3,] "protest" "commission" "govern" "secretari" "organis"
[4,] "friend" "mother" "boy" "kill" "love"
[5,] "royal" "state" "ireland" "northern" "minist"
# Create label for color legend
legend_label_stm <- paste("Top 5 Terms\n",
"1: ", paste(top_terms_stm$frex[1,], collapse = ", "), "\n",
"2: ", paste(top_terms_stm$frex[2,], collapse = ", "), "\n",
"3: ", paste(top_terms_stm$frex[3,], collapse = ", "), "\n",
"4: ", paste(top_terms_stm$frex[4,], collapse = ", "), "\n",
"5: ", paste(top_terms_stm$frex[5,], collapse = ", "), sep = "")
# Plot the topic proportions over time
ggplot(topic_proportions_stm_half_year, aes(x = half_year, y = proportion, color = factor(Topic_stm))) +
geom_line() +
labs(x = "Half-Year", y = "Proportion",
color = legend_label_stm,
subtitle = "Topic Changes Over Time Using Structural Topic Modeling (STM)") +
theme_bw()

# Try to combine sentiment and topic changes over time
library(patchwork)
# Create a line chart of sentiment score over time
p1 <- ggplot(sentiment_by_date, aes(x = first_publication_date, y = sentiment_score)) +
geom_line() +
geom_hline(yintercept = 0, linetype = "dashed", color = "blue") +
labs(title = "Sentiment Score over Time Based on Syuzhet Package", x = "Date", y = "Sentiment Score") +
theme_minimal()+
theme(plot.title = element_text(size = 11))
# Plot the topic proportions over time using LDA
p2 <- ggplot(topic_proportions_half_year, aes(x = half_year, y = proportion, color = factor(Topic_lda))) +
geom_line() +
labs(x = "Half-Year", y = "Proportion",
color = "Topic", subtitle = "Topic Changes Over Time Using LDA Topic Modelling") +
theme_bw()
# Plot the topic proportions over time using STM
p3 <- ggplot(topic_proportions_stm_half_year, aes(x = half_year, y = proportion, color = factor(Topic_stm))) +
geom_line() +
labs(x = "Half-Year", y = "Proportion",
color = "Topic",
subtitle = "Topic Changes Over Time Using Structural Topic Modeling") +
theme_bw()
# Combine the three plots vertically
p1 + p2 + p3 + plot_layout(ncol = 1)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpgYGB7cn0KIyBDb25uZWN0IHRvIEd1YXJkaWFuIEFQSQpsaWJyYXJ5KGd1YXJkaWFuYXBpKQpndV9hcGlfa2V5KCkKYGBgCgpgYGB7cn0KIyBHZXQgZGF0YSBpbmNsdWRlIGtleSB3b3JkcyAicG9saWNlIiBPUiAibGF3IGVuZm9yY2VtZW50IgpkYXRhX2FsbCA8LWd1X2NvbnRlbnQocXVlcnk9JyJwb2xpY2UiIE9SICJsYXcgZW5mb3JjZW1lbnQiJywKICAgICAgICBmcm9tX2RhdGU9IjIwMjEtMDEtMDEiLAogICAgICAgIHRvX2RhdGU9IjIwMjMtMDMtMzEiKQpgYGAKYGBge3J9CiMgQ2hlY2sgdGhlIGRhdGEKZGltKGRhdGFfYWxsKQpoZWFkKGRhdGFfYWxsKQojIDI0ODYzIG5ld3MgYXJ0aWNsZXMgY29sbGVjdGVkCmBgYAoKYGBge3J9CiMgTGltaXQgdGhlIGRhdGEgdG8gb25seSBzZWN0aW9uX25hbWUgPT0gJ1VLIG5ld3MnCmRhdGFfdWtfc2VjIDwtIHN1YnNldChkYXRhX2FsbCwgc2VjdGlvbl9uYW1lID09ICdVSyBuZXdzJykKZGltKGRhdGFfdWtfc2VjKQpoZWFkKGRhdGFfdWtfc2VjKSAKYGBgCgoKYGBge3J9CiMgU2F2ZSBhcyBDU1YgZmlsZQpsaWJyYXJ5KGRwbHlyKQpteV9kYXRhIDwtIGRhdGFfdWtfc2VjICU+JSAKICBzZWxlY3QoLXRhZ3MpICMgcmVtb3ZlIHRoZSBub24taW5mb3JtYXRpdmUgY29sdW1uIHRoYXQgY29udGFpbnMgbmVzdGVkIGxpc3RzCm15X2RmIDwtIGFzLmRhdGEuZnJhbWUobXlfZGF0YSkKd3JpdGUuY3N2KG15X2RmLCAicmF3X2RhdGEuY3N2Iiwgcm93Lm5hbWVzID0gVFJVRSkKYGBgCgpgYGB7cn0KIyBSZWFkIGNvbGxlY3RlZCBkYXRhCm15X2RhdGEgPC0gcmVhZC5jc3YoJ3Jhd19kYXRhLmNzdicpCmRpbShteV9kYXRhKSMgMzI1OSBVSyBuZXdzIGFydGljbGVzIApgYGAKCmBgYHtyfQojIExvYWQgcmVxdWlyZWQgcGFja2FnZXMKbGlicmFyeSh0aWR5cikKbGlicmFyeShxdWFudGVkYSkKbGlicmFyeShjYXJldCkKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5dGV4dCkKbGlicmFyeShzeXV6aGV0KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShxdWFudGVkYS50ZXh0c3RhdHMpCmxpYnJhcnkoZ2dwbG90MikKYGBgCgpgYGB7cn0KIyBQcmUtcHJvY2Vzc2luZwojIFNlbGVjdCByZXF1aXJlZCBjb2x1bW5zCm15X2RhdGFfaGJkIDwtIG15X2RhdGEgJT4lIAogIHNlbGVjdChoZWFkbGluZSwgYm9keV90ZXh0LCBmaXJzdF9wdWJsaWNhdGlvbl9kYXRlKQojIFJlbW92ZSBkdXBsaWNhdGVzCm15X2RhdGFfaGJkICAgPC0gZGlzdGluY3QobXlfZGF0YV9oYmQpCiMgUmVtb3ZlIG5vaXNlCm15X2RhdGFfaGJkJGJvZHlfdGV4dCA8LSBnc3ViKCJcXFcrIiwgIiAiLCBteV9kYXRhX2hiZCRib2R5X3RleHQpCiMgVG9rZW5pc2UgdGhlIGRhdGEgYW5kIHJlbW92ZSBwdW5jdHVhdGlvbiwgbnVtYmVycywgYW5kIHN5bWJvbHMKIyBDb252ZXJ0IGFsbCB0ZXh0IHRvIGxvd2VyY2FzZSBhbmQgYXBwbHkgc3RlbW1pbmcKbXlfZGF0YV90b2tlbnMgPC0gdG9rZW5zKG15X2RhdGFfaGJkJGJvZHlfdGV4dCwgcmVtb3ZlX3B1bmN0ID0gVFJVRSwgcmVtb3ZlX251bWJlcnMgPSBUUlVFLCByZW1vdmVfc3ltYm9scyA9IFRSVUUpICU+JSB0b2tlbnNfcmVtb3ZlKHN0b3B3b3JkcygiZW5nbGlzaCIpKSAlPiUgdG9rZW5zX3RvbG93ZXIoKSAlPiUgdG9rZW5zX3dvcmRzdGVtKGxhbmd1YWdlID0gcXVhbnRlZGFfb3B0aW9ucygibGFuZ3VhZ2Vfc3RlbW1lciIpKSAKYGBgCgpgYGB7cn0KZGltKG15X2RhdGFfaGJkKSMgMzE5NCBVSyBuZXdzIGFydGljbGVzIGFmdGVyIHByZS1wcm9jZXNzaW5nCmhlYWQobXlfZGF0YV9oYmQpCmBgYAoKCmBgYHtyfQojIyBCaW5nIGxleGljb24KIyBUcnkgdG8gdXNlICJiaW5nIiBsZXhpY29uIHRvIGdldCBzZW50aW1lbnQgc2NvcmUgZm9yIGVhY2ggbmV3cyBhcnRpY2xlCiMgQ29udmVydCB0aGUgZGF0YSB0byBhIHRpZHkgZm9ybWF0CnRva2VucyA8LSBteV9kYXRhX2hiZCAlPiUKICB1bm5lc3RfdG9rZW5zKHdvcmQsIGJvZHlfdGV4dCkgJT4lCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpCiMgTG9hZCB0aGUgbGV4aWNvbgpsZXhpY29uIDwtIGdldF9zZW50aW1lbnRzKCJiaW5nIikKCiMgSm9pbiB0aGUgbGV4aWNvbiB3aXRoIGRhdGEKbXlfZGF0YV9zZW50aW1lbnQgPC0gdG9rZW5zICU+JQogIGlubmVyX2pvaW4obGV4aWNvbikKCiMgQ2FsY3VsYXRlIHRoZSBzZW50aW1lbnQgc2NvcmUKbXlfZGF0YV9zZW50aW1lbnRfc2NvcmVzIDwtIG15X2RhdGFfc2VudGltZW50ICU+JQogIGNvdW50KGhlYWRsaW5lLCBzZW50aW1lbnQpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzZW50aW1lbnQsIHZhbHVlc19mcm9tID0gbiwgdmFsdWVzX2ZpbGwgPSAwKSAlPiUKICBtdXRhdGUoc2VudGltZW50X3Njb3JlID0gcG9zaXRpdmUgLSBuZWdhdGl2ZSkKCiMgVmlzdWFsaXNlIHRoZSBzZW50aW1lbnQgc2NvcmVzCmdncGxvdChteV9kYXRhX3NlbnRpbWVudF9zY29yZXMsIGFlcyh4ID0gc2VudGltZW50X3Njb3JlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMiwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBTZW50aW1lbnQgU2NvcmVzIiwKICAgICAgIHggPSAiU2VudGltZW50IFNjb3JlIiwKICAgICAgIHkgPSAiRnJlcXVlbmN5IikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBDYXRlZ29yaXNlIHNlbnRpbWVudCBzY29yZXMgYXMgcG9zaXRpdmUsIG5lZ2F0aXZlLCBvciBuZXV0cmFsCm15X2RhdGFfc2VudGltZW50X2NhdGVnb3J5IDwtIG15X2RhdGFfc2VudGltZW50X3Njb3JlcyAlPiUKICBtdXRhdGUoc2VudGltZW50X2NhdGVnb3J5ID0gaWZlbHNlKHNlbnRpbWVudF9zY29yZSA+IDAsICJQb3NpdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc2VudGltZW50X3Njb3JlIDwgMCwgIk5lZ2F0aXZlIiwgIk5ldXRyYWwiKSkpCiAgCiMgQ291bnQgdGhlIG51bWJlciBvZiBhcnRpY2xlcyBpbiBlYWNoIHNlbnRpbWVudCBjYXRlZ29yeQpteV9kYXRhX3NlbnRpbWVudF9jb3VudHMgPC0gbXlfZGF0YV9zZW50aW1lbnRfY2F0ZWdvcnkgJT4lCiAgY291bnQoc2VudGltZW50X2NhdGVnb3J5KQpwcmludChteV9kYXRhX3NlbnRpbWVudF9jb3VudHMpCmBgYAoKYGBge3J9CiMgVmlzdWFsaXNlIHRoZSBkaXN0cmlidXRpb24gb2Ygc2VudGltZW50IGNhdGVnb3JpZXMgaW4gYXJ0aWNsZXMKZ2dwbG90KGRhdGEgPSBteV9kYXRhX3NlbnRpbWVudF9jb3VudHMsIGFlcyh4ID0gc2VudGltZW50X2NhdGVnb3J5LCB5ID0gbiwgZmlsbCA9IHNlbnRpbWVudF9jYXRlZ29yeSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbiksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArCiAgbGFicyh4ID0gIlNlbnRpbWVudCBDYXRlZ29yeSIsIHkgPSAiTnVtYmVyIG9mIEFydGljbGVzIiwgZmlsbCA9ICJTZW50aW1lbnQgQ2F0ZWdvcnkiKSArCiAgZ2d0aXRsZSgiU2VudGltZW50IENhdGVnb3JpZXMgUHJvcG9ydGlvbnMgQmFzZWQgb24gQmluZyBMZXhpY29uIikKCmBgYAoKYGBge3J9CiMgVHJ5IHRvIGV4cGxvcmUgc2VudGltZW50IGNoYW5nZSBvdmVyIHRpbWUgYmFzZWQgb24gQmluZyBsZXhpY29uCiMgQ2FsY3VsYXRlIHRoZSBzZW50aW1lbnQgc2NvcmUgYnkgZGF0ZQpzZW50aW1lbnRfc2NvcmVzX2RhdGUgPC0gbXlfZGF0YV9zZW50aW1lbnQgJT4lCiAgY291bnQoZmlyc3RfcHVibGljYXRpb25fZGF0ZSwgc2VudGltZW50KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc2VudGltZW50LCB2YWx1ZXNfZnJvbSA9IG4sIHZhbHVlc19maWxsID0gMCkgJT4lCiAgbXV0YXRlKHNlbnRpbWVudF9zY29yZSA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpCgojIENhdGVnb3Jpc2Ugc2VudGltZW50IHNjb3JlcyBhcyBwb3NpdGl2ZSwgbmVnYXRpdmUsIG9yIG5ldXRyYWwKc2VudGltZW50X2NhdGVnb3J5X2RhdGUgPC0gc2VudGltZW50X3Njb3Jlc19kYXRlICU+JQogIG11dGF0ZShzZW50aW1lbnRfY2F0ZWdvcnkgPSBpZmVsc2Uoc2VudGltZW50X3Njb3JlID4gMCwgIlBvc2l0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzZW50aW1lbnRfc2NvcmUgPCAwLCAiTmVnYXRpdmUiLCAiTmV1dHJhbCIpKSkKCiMgQ29udmVydCBkYXRlIHRvIGRhdGUgb2JqZWN0CnNlbnRpbWVudF9jYXRlZ29yeV9kYXRlJGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUgPC0gYXMuRGF0ZShzZW50aW1lbnRfY2F0ZWdvcnlfZGF0ZSRmaXJzdF9wdWJsaWNhdGlvbl9kYXRlKQoKIyBWaXN1YWxpc2UgdGhlIHNlbnRpbWVudCBzY29yZXMgb3ZlciB0aW1lCmdncGxvdChzZW50aW1lbnRfY2F0ZWdvcnlfZGF0ZSwgYWVzKHggPSBmaXJzdF9wdWJsaWNhdGlvbl9kYXRlLCB5ID0gc2VudGltZW50X3Njb3JlKSkgKwogIGdlb21fbGluZShjb2xvciA9ICIjMDAwMDgwIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIlNlbnRpbWVudCBTY29yZSBPdmVyIFRpbWUgQmFzZWQgb24gQmluZyBMZXhpY29uIiwKICAgICAgIHggPSAiRGF0ZSIsCiAgICAgICB5ID0gIlNlbnRpbWVudCBTY29yZSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKYGBge3J9CiMjIFVzZSBnZXRfc2VudGltZW50KCkgZnJvbSBzeXV6aGV0IHBhY2thZ2UgdG8gZ2V0IHNlbnRpbWVudCBzY29yZSBmb3IgZWFjaCBuZXdzIGFydGljbGUKbGlicmFyeShzeXV6aGV0KQojIENhbGN1bGF0ZSB0aGUgc2VudGltZW50IHNjb3JlcwpteV9kYXRhX2hiZCRzZW50aW1lbnRfc2NvcmUgPC0gZ2V0X3NlbnRpbWVudChteV9kYXRhX2hiZCRib2R5X3RleHQpCgojIENhdGVnb3Jpc2Ugc2VudGltZW50IHNjb3JlcyBhcyBwb3NpdGl2ZSwgbmVnYXRpdmUsIG9yIG5ldXRyYWwKbXlfZGF0YV9oYmQkc2VudGltZW50X2NhdGVnb3J5IDwtIGN1dChteV9kYXRhX2hiZCRzZW50aW1lbnRfc2NvcmUsIGJyZWFrcz1jKC1JbmYsLTAuMSwwLjEsSW5mKSwgbGFiZWxzPWMoIk5lZ2F0aXZlIiwgIk5ldXRyYWwiLCAiUG9zaXRpdmUiKSkKCiMgVmlzdWFsaXNlIHRoZSBkaXN0cmlidXRpb24gb2Ygc2VudGltZW50IHNjb3JlcwpnZ3Bsb3QobXlfZGF0YV9oYmQsIGFlcyh4PXNlbnRpbWVudF9zY29yZSwgZmlsbD1zZW50aW1lbnRfY2F0ZWdvcnkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC4wNSwgYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiKSArCiAgbGFicyh0aXRsZT0iU2VudGltZW50IFNjb3JlcyBEaXN0cmlidXRpb24iLCB4PSJTZW50aW1lbnQgU2NvcmUiLCB5PSJDb3VudCIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7cn0KIyBWaXN1YWxpc2UgdGhlIHByb3BvcnRpb24gb2YgcG9zaXRpdmUsIG5lZ2F0aXZlLCBhbmQgbmV1dHJhbCBzZW50aW1lbnQgY2F0ZWdvcmllcwpnZ3Bsb3QobXlfZGF0YV9oYmQsIGFlcyh4ID0gc2VudGltZW50X2NhdGVnb3J5LCBmaWxsID0gc2VudGltZW50X2NhdGVnb3J5KSkgKwogIGdlb21fYmFyKCkgKwogIGdlb21fdGV4dChzdGF0ID0gImNvdW50IiwgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCAKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsKICBsYWJzKHRpdGxlID0gIlNlbnRpbWVudCBDYXRlZ29yaWVzIFByb3BvcnRpb25zIEJhc2VkIG9uIFN5dXpoZXQgUGFja2FnZSIsCiAgICAgICB4ID0gIlNlbnRpbWVudCBDYXRlZ29yeSIsIHkgPSAiTnVtYmVyIG9mIEFydGljbGVzIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZXk5MCIsIGNvbG9yID0gTkEpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAid2hpdGUiKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQoKYGBgCgoKYGBge3J9CiMgVHJ5IHRvIGV4cGxvcmUgc2VudGltZW50IGNoYW5nZSBvdmVyIHRpbWUgYmFzZWQgb24gc3l1emhldCBwYWNrYWdlCiMgQ29udmVydCBmaXJzdF9wdWJsaWNhdGlvbl9kYXRlIHRvIGEgZGF0ZSBvYmplY3QKbXlfZGF0YV9oYmQkZmlyc3RfcHVibGljYXRpb25fZGF0ZSA8LSBhcy5EYXRlKG15X2RhdGFfaGJkJGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUpCgojIEFnZ3JlZ2F0ZSBzZW50aW1lbnQgc2NvcmUgYnkgZGF0ZQpzZW50aW1lbnRfYnlfZGF0ZSA8LSBteV9kYXRhX2hiZCAlPiUKICBncm91cF9ieShmaXJzdF9wdWJsaWNhdGlvbl9kYXRlKSAlPiUKICBzdW1tYXJpc2Uoc2VudGltZW50X3Njb3JlID0gbWVhbihzZW50aW1lbnRfc2NvcmUpKQoKIyBDcmVhdGUgYSBsaW5lIGNoYXJ0IG9mIHNlbnRpbWVudCBzY29yZSBvdmVyIHRpbWUKZ2dwbG90KHNlbnRpbWVudF9ieV9kYXRlLCBhZXMoeD1maXJzdF9wdWJsaWNhdGlvbl9kYXRlLCB5PXNlbnRpbWVudF9zY29yZSkpICsKICBnZW9tX2xpbmUoY29sb3IgPSAiIzAwMDA4MCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvciA9ICJyZWQiKSArCiAgbGFicyh0aXRsZT0iU2VudGltZW50IFNjb3JlIG92ZXIgVGltZSBCYXNlZCBvbiBTeXV6aGV0IFBhY2thZ2UiLCB4PSJEYXRlIiwgeT0iU2VudGltZW50IFNjb3JlIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCgpgYGB7cn0KIyBQbG90IHRoZSBzZW50aW1lbnQgc2NvcmUgb3ZlciB0aW1lIHRvZ2V0aGVyIChCaW5nIExleGljb24gYW5kIFN5dXpoZXQgUGFja2FnZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoc2NhbGVzKQoKIyBDcmVhdGUgdGhlIGZpcnN0IHBsb3QKcGxvdGEgPC0gZ2dwbG90KHNlbnRpbWVudF9jYXRlZ29yeV9kYXRlLCBhZXMoeCA9IGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUsIHkgPSBzZW50aW1lbnRfc2NvcmUpKSArCiAgZ2VvbV9saW5lKGNvbG9yID0gIiMwMDAwODAiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAicmVkIikgKwogIGxhYnModGl0bGUgPSAiU2VudGltZW50IFNjb3JlIE92ZXIgVGltZSBCYXNlZCBvbiBCaW5nIExleGljb24iLAogICAgICAgeCA9ICJEYXRlIiwKICAgICAgIHkgPSAiU2VudGltZW50IFNjb3JlIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjIgbW9udGhzIiwgZGF0ZV9sYWJlbHMgPSAiJWIgJXkiKQoKIyBDcmVhdGUgdGhlIHNlY29uZCBwbG90CnBsb3RiIDwtIGdncGxvdChzZW50aW1lbnRfYnlfZGF0ZSwgYWVzKHg9Zmlyc3RfcHVibGljYXRpb25fZGF0ZSwgeT1zZW50aW1lbnRfc2NvcmUpKSArCiAgZ2VvbV9saW5lKGNvbG9yID0gIiMwMDAwODAiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAicmVkIikgKwogIGxhYnModGl0bGU9IlNlbnRpbWVudCBTY29yZSBvdmVyIFRpbWUgQmFzZWQgb24gU3l1emhldCBQYWNrYWdlIiwgeD0iRGF0ZSIsIHk9IlNlbnRpbWVudCBTY29yZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIyIG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiViICV5IikKCiMgQ29tYmluZSB0aGUgcGxvdHMgdmVydGljYWxseSB1c2luZyBwYXRjaHdvcmsKcGxvdGEgLyBwbG90YgpgYGAKCgpgYGB7cn0KIyMgU3VwZXJ2aXNlZCBNYWNoaW5lIExlYXJuaW5nCiMgVHJ5IHRvIGFwcGx5IG1hY2hpbmUgbGVhcm5pbmcgdGVjaG5pcXVlLCBidWlsZCBhIG1vZGVsIHRvIHByZWRpY3Qgc2VudGltZW50CiMgTWFwIHRoZSB2YWx1ZXMgb2YgdGhlIG9yaWdpbmFsIGNvbHVtbiAic2VudGltZW50X2NhdGVnb3J5IiB0byBpbnRlZ2VycwpteV9kYXRhX21sIDwtIG15X2RhdGFfaGJkICU+JQogIG11dGF0ZShpZCA9IHJvd19udW1iZXIoKSwgIyBhZGQgYSBuZXcgY29sdW1uIHdpdGggcm93IG51bWJlcnMKICAgICAgICAgc2VudGltZW50X2NhdGVnb3J5ID0gY2FzZV93aGVuKAogICAgICAgICAgIHNlbnRpbWVudF9jYXRlZ29yeSA9PSAiUG9zaXRpdmUiIH4gMiwKICAgICAgICAgICBzZW50aW1lbnRfY2F0ZWdvcnkgPT0gIk5lZ2F0aXZlIiB+IDEsCiAgICAgICAgICAgc2VudGltZW50X2NhdGVnb3J5ID09ICJOZXV0cmFsIiB+IDAsCiAgICAgICAgICAgVFJVRSB+IE5BX3JlYWxfICMgdG8gaGFuZGxlIGFueSBvdGhlciBjYXNlcwogICAgICAgICApKSAKYGBgCgpgYGB7cn0KIyBFeHRyYWN0IHVuaWdyYW1zLCBiaWdyYW1zIGFzIGZlYXR1cmVzCnVuaWdyYW1zIDwtIG15X2RhdGFfbWwkYm9keV90ZXh0ICU+JQogICAgICAgICAgdG9rZW5zKHJlbW92ZV9wdW5jdCA9IFQpICU+JQogICAgICAgICAgdG9rZW5zX25ncmFtcygxKSAlPiUKICAgICAgICAgIGRmbSgpCmJpZ3JhbXMgPC0gIG15X2RhdGFfbWwkYm9keV90ZXh0ICU+JQogICAgICAgICAgdG9rZW5zKHJlbW92ZV9wdW5jdCA9IFQpICU+JQogICAgICAgICAgdG9rZW5zX25ncmFtcygyKSAlPiUKICAgICAgICAgIGRmbSgpCiMgQXBwbHkgc3BhcnNpdHkgY29ycmVjdGlvbiB0byByZWR1Y2UgdGhlIHplcm8gY291bnRzCnVuaWdyYW1zX2NvcnJlY3RlZCA8LSBkZm1fdHJpbSh1bmlncmFtcywgc3BhcnNpdHkgPSAuOTApCmJpZ3JhbXNfY29ycmVjdGVkIDwtIGRmbV90cmltKGJpZ3JhbXMsIHNwYXJzaXR5ID0gLjkwKQoKIyBDaGVjayBkaW1lbnNpb25zCmRpbSh1bmlncmFtc19jb3JyZWN0ZWQpCgojIFRGSURGLXdlaWdodGVkIG5ncmFtcwp1bmlncmFtc190ZmlkZiA8LSBkZm1fdGZpZGYodW5pZ3JhbXNfY29ycmVjdGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY2hlbWVfdGYgPSAnY291bnQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY2hlbWVfZGYgPSAnaW52ZXJzZScpCmJpZ3JhbXNfdGZpZGYgPC0gZGZtX3RmaWRmKGJpZ3JhbXNfY29ycmVjdGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNjaGVtZV90ZiA9ICdjb3VudCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2NoZW1lX2RmID0gJ2ludmVyc2UnKQoKIyBDb252ZXJ0IG5ncmFtcyB0byBkYXRhIGZyYW1lcwpuZXdzX3VuaWdyYW1zIDwtIGNvbnZlcnQodW5pZ3JhbXNfdGZpZGYsICdkYXRhLmZyYW1lJykKbmV3c19iaWdyYW1zIDwtIGNvbnZlcnQoYmlncmFtc190ZmlkZiwgJ2RhdGEuZnJhbWUnKQoKIyBBZGQgaWQgY29sdW1uIHRvIG5ncmFtcyB0byBqb2luIHVuaWdyYW1zIGFuZCBiaWdyYW1zIGxhdGVyCm5ld3NfdW5pZ3JhbXMkaWQgPC0gbXlfZGF0YV9tbCRpZApuZXdzX2JpZ3JhbXMkaWQgPC0gbXlfZGF0YV9tbCRpZAoKIyBSZW1vdmUgZG9jX2lkIGNvbHVtbgp1bmlncmFtc190ZmlkZl9kZiA8LSBzdWJzZXQobmV3c191bmlncmFtcywgc2VsZWN0ID0gLWMoZG9jX2lkKSkKYmlncmFtc190ZmlkZl9kZiA8LSBzdWJzZXQobmV3c19iaWdyYW1zLCBzZWxlY3QgPSAtYyhkb2NfaWQpKQoKIyBKb2luIHRoZSB0d28gZGF0YXNldHMKbWxfZGF0YSA8LSBiaWdyYW1zX3RmaWRmX2RmICU+JSBsZWZ0X2pvaW4odW5pZ3JhbXNfdGZpZGZfZGYsIGJ5ID0gImlkIikKYGBgCgpgYGB7cn0KIyBHZXQgc2VudGltZW50IHNjb3JlcyBhcyBmZWF0dXJlcwptbF9kYXRhJHNlbnRpbWVudF9zY29yZSA8LSBnZXRfc2VudGltZW50KG15X2RhdGFfbWwkYm9keV90ZXh0KQojIEFkZCBsYWJlbCBjb2x1bW4KbWxfZGF0YSRsYWJlbCA8LSBteV9kYXRhX21sJHNlbnRpbWVudF9jYXRlZ29yeQojIFJlbW92ZSB0aGUgaWQgY29sdW1uCm1sX2RhdGEgPC0gbWxfZGF0YSAlPiUgc2VsZWN0KC1pZCkKIyBNYXAgbGFiZWxzCm1sX2RhdGEkbGFiZWwgPC0gaWZlbHNlKG1sX2RhdGEkbGFiZWwgPT0gIjIiLCAiUG9zaXRpdmUiLCAKICAgICAgICAgICAgICAgICAgIGlmZWxzZShtbF9kYXRhJGxhYmVsID09ICIxIiwgIk5lZ2F0aXZlIiwgIk5ldXRyYWwiKSkgCiMgQ29udmVydCBsYWJlbCB0byBmYWN0b3IKbWxfZGF0YSRsYWJlbCA8LSBhcy5mYWN0b3IobWxfZGF0YSRsYWJlbCkgIyBjb252ZXJ0IGxhYmVsIHRvIGZhY3RvcgpgYGAKCmBgYHtyfQojIEFwcGx5IG1hY2hpbmUgbGVhcm5pbmcgc2V0dGluZwpsaWJyYXJ5KGNhcmV0KQpzZXQuc2VlZCgxMjMpCmZvcl90cmFpbmluZyA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBtbF9kYXRhJGxhYmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcCA9IC44LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFKQoKdHJhaW5pbmdfZGF0YSA8LSBtbF9kYXRhW2Zvcl90cmFpbmluZyxdCnRlc3RpbmdfZGF0YSA8LSBtbF9kYXRhWy1mb3JfdHJhaW5pbmcsXQpgYGAKCgpgYGB7cn0KIyBTZXQgdHJhaW5pbmcgY29udHJvbCBwYXJhbWV0ZXJzCnRyYWluaW5nX2NvbnRyb2xzIDwtIHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzUHJvYnMgPSBUKQpgYGAKCmBgYHtyfQojIyBUcmFpbiB0aGUgU1ZNIG1vZGVsCm1vZGVsLnN2bSA8LSB0cmFpbihsYWJlbCB+IC4sCiAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbmluZ19kYXRhLAogICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtTGluZWFyIiwKICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5pbmdfY29udHJvbHMsCiAgICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCd6dicpCiAgICAgICAgICAgICAgICAgKQpgYGAKCgpgYGB7cn0KIyBQcmVkaWN0IG9uIHRoZSB0ZXN0aW5nIGRhdGEKcHJlZF9zdm0gPC0gcHJlZGljdChtb2RlbC5zdm0sIHRlc3RpbmdfZGF0YSkKCiMgRXZhbHVhdGUgdGhlIG1vZGVsCmNvbmZ1c2lvbk1hdHJpeChwcmVkX3N2bSwgYXMuZmFjdG9yKHRlc3RpbmdfZGF0YSRsYWJlbCksIG1vZGU9InByZWNfcmVjYWxsIikKYGBgCgoKYGBge3J9CiMjIFRyYWluIHRoZSBOYWl2ZSBCYXllcyBtb2RlbAptb2RlbC5uYiA8LSB0cmFpbihsYWJlbCB+IC4sCiAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbmluZ19kYXRhLAogICAgICAgICAgICAgICAgICBtZXRob2QgPSAibmFpdmVfYmF5ZXMiLAogICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbmluZ19jb250cm9scywKICAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoJ3p2JykpCmBgYAoKCmBgYHtyfQojIFByZWRpY3Qgb24gdGhlIHRlc3RpbmcgZGF0YQpwcmVkX25iIDwtIHByZWRpY3QobW9kZWwubmIsIHRlc3RpbmdfZGF0YSkKCiMgRXZhbHVhdGUgdGhlIG1vZGVsCmNvbmZ1c2lvbk1hdHJpeChwcmVkX25iLCBhcy5mYWN0b3IodGVzdGluZ19kYXRhJGxhYmVsKSwgbW9kZT0icHJlY19yZWNhbGwiKQpgYGAKCmBgYHtyfQojIFNldCB0cmFpbmluZyBjb250cm9sIHBhcmFtZXRlcnMKdHJhaW5pbmdfY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlSXRlciA9IEZBTFNFKQpgYGAKCmBgYHtyfQojIFRyYWluIHRoZSBOZXVyYWwgTmV0d29yayAoTk4pIG1vZGVsCm1vZGVsLm5uIDwtIHRyYWluKGxhYmVsIH4gLiwKICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nX2RhdGEsCiAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluaW5nX2NvbnRyb2wsCiAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJubmV0IiwKICAgICAgICAgICAgICAgICAgTWF4Tld0cyA9IDEwMDAwLAogICAgICAgICAgICAgICAgICBtYXhpdCA9IDEwKQpgYGAKCmBgYHtyfQojIFByZWRpY3Qgb24gdGhlIHRlc3RpbmcgZGF0YQpwcmVkX25uIDwtIHByZWRpY3QobW9kZWwubm4sIHRlc3RpbmdfZGF0YSkKIyBFdmFsdWF0ZSB0aGUgbW9kZWwKY29uZnVzaW9uTWF0cml4KHByZWRfbm4sIHRlc3RpbmdfZGF0YSRsYWJlbCwgbW9kZSA9ICJwcmVjX3JlY2FsbCIpCmBgYAoKCmBgYHtyfQojIyBVbnN1cGVydmlzZWQgTWFjaGluZSBMZWFybmluZwojIFRyeSB0byBhcHBseSBrLW1lYW5zIGNsdXN0ZXJpbmcgbW9kZWwgdXNpbmcgdGhlIGV4dHJhY3RlZCBmZWF0dXJlcwojIFVzZSBuZ3JhbXMgZnJlcXVlbmNpZXMgYXMgZmVhdHVyZXMKbWxfZ3JhbXMgPC0gYmlncmFtc190ZmlkZl9kZiAlPiUgbGVmdF9qb2luKHVuaWdyYW1zX3RmaWRmX2RmLCBieSA9ICJpZCIpCiMgUmVtb3ZlIHRoZSBpZCBjb2x1bW4KbWxfZ3JhbXMgPC0gbWxfZ3JhbXMgJT4lIHNlbGVjdCgtaWQpCmBgYAoKYGBge3J9CiMgVXNlIHRoZSBrLW1lYW5zIG1vZGVsIG9uIG4tZ3JhbXMKIyBEZXRlcm1pbmUgawp3c3MgPC0gbnVtZXJpYygpCmZvcihpIGluIDE6MTApewogIG5ncmFtc19rbWVhbnNfdGVtcCA8LSBrbWVhbnMoeCA9IG1sX2dyYW1zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbnRlcnMgPSBpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGl0ZXIubWF4ID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnN0YXJ0ID0gMTApCiAgd3NzW2ldIDwtIG5ncmFtc19rbWVhbnNfdGVtcCR0b3Qud2l0aGluc3MKfQp7cGxvdCh3c3MsIHR5cGU9J2InKX0gCgpgYGAKCmBgYHtyfQojIFNldHRsZSBmb3Igaz0yCm5ncmFtc19rbWVhbnMgPC0ga21lYW5zKG1sX2dyYW1zLAogICAgICAgICAgICAgICAgICAgICAgY2VudGVycyA9IDIsCiAgICAgICAgICAgICAgICAgICAgICBpdGVyLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgbnN0YXJ0ID0gMTApCmBgYAoKYGBge3J9CiMgTG9vayBhdCB0aGUgY2x1c3RlcnMgYnkgdGhlaXIgbW9zdCBwcm9taW5lbnQgbmdyYW0gZGlmZmVyZW5jZXMKIyBDbHVzdGVyIDEKY2F0KCJDbHVzdGVyIDE6IFxuIikKcHJpbnQoaGVhZChzb3J0KG5ncmFtc19rbWVhbnMkY2VudGVyc1sxLCBdLCBkZWNyZWFzaW5nID0gVFJVRSksIG4gPSAyMCkpCiMgQ2x1c3RlciAyCmNhdCgiXG5DbHVzdGVyIDI6IFxuIikKcHJpbnQoaGVhZChzb3J0KG5ncmFtc19rbWVhbnMkY2VudGVyc1syLCBdLCBkZWNyZWFzaW5nID0gVFJVRSksIG4gPSAyMCkpCmBgYAoKYGBge3J9CiMgVXNlIHRoZSBrLW1lYW5zIG1vZGVsIG9uIHNlbnRpbWVudCBzY29yZQojIERldGVybWluZSBrCndzcyA8LSBudW1lcmljKCkKZm9yKGkgaW4gMToxMCl7CiAgc2VudGltZW50X2ttZWFuc190ZW1wIDwtIGttZWFucyh4ID0gbWxfZGF0YSRzZW50aW1lbnRfc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVycyA9IGksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlci5tYXggPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc3RhcnQgPSAxMCkKICB3c3NbaV0gPC0gc2VudGltZW50X2ttZWFuc190ZW1wJHRvdC53aXRoaW5zcwp9CntwbG90KHdzcywgdHlwZT0nYicpfQpgYGAKCgpgYGB7cn0KIyBTZXR0bGUgZm9yIGs9MwpzZW50aW1lbnRfa21lYW5zIDwtIGttZWFucyhtbF9kYXRhJHNlbnRpbWVudF9zY29yZSwKICAgICAgICAgICAgICAgICAgICAgIGNlbnRlcnMgPSAzLAogICAgICAgICAgICAgICAgICAgICAgaXRlci5tYXggPSA1LAogICAgICAgICAgICAgICAgICAgICAgbnN0YXJ0ID0gNSkKYGBgCgoKYGBge3J9CiMgUHJpbnQgdGhlIGNlbnRlcnMgb2YgZWFjaCBzZW50aW1lbnQgY2x1c3RlcgpwcmludChzZW50aW1lbnRfa21lYW5zJGNlbnRlcnNbMSwgXSkKcHJpbnQoc2VudGltZW50X2ttZWFucyRjZW50ZXJzWzIsIF0pCnByaW50KHNlbnRpbWVudF9rbWVhbnMkY2VudGVyc1szLCBdKQpgYGAKCmBgYHtyfQojIFZpc3VhbGlzZSB0aGUgY2x1c3RlcnMKIyBBZGQgdGhlIGNsdXN0ZXIgbGFiZWxzIHRvIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lCm1sX2RhdGEkY2x1c3RlciA8LSBmYWN0b3Ioc2VudGltZW50X2ttZWFucyRjbHVzdGVyKQoKIyBDcmVhdGUgdGhlIGJveHBsb3QKZ2dwbG90KG1sX2RhdGEsIGFlcyh4ID0gY2x1c3RlciwgeSA9IHNlbnRpbWVudF9zY29yZSkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgeGxhYigiQ2x1c3RlciIpICsKICB5bGFiKCJTZW50aW1lbnQgU2NvcmUiKQoKYGBgCgoKYGBge3J9CiMjIyBUb3BpYyBNb2RlbGxpbmcKIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocXVhbnRlZGEpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkodG9waWNtb2RlbHMpCmxpYnJhcnkobGRhdHVuaW5nKQpsaWJyYXJ5KHN0bSkKbGlicmFyeSh3b3JkY2xvdWQpCmxpYnJhcnkodG0pCmxpYnJhcnkoZ2dwbG90MikKYGBgCgpgYGB7cn0KIyBQcmVwYXJlIHRoZSBkYXRhIGFuZCBzZWxlY3QgcmVxdWlyZWQgY29sdW1ucwpteV9kYXRhX2hiZCA8LSBteV9kYXRhICU+JSAKICBzZWxlY3QoaGVhZGxpbmUsIGJvZHlfdGV4dCwgZmlyc3RfcHVibGljYXRpb25fZGF0ZSkKIyBSZW1vdmUgZHVwbGljYXRlcwpteV9kYXRhX2hiZCA8LSBkaXN0aW5jdChteV9kYXRhX2hiZCkKIyBSZW1vdmUgYW55IG5vaXNlIGluIHRoZSBkYXRhCm15X2RhdGFfaGJkJGJvZHlfdGV4dCA8LSBnc3ViKCJcXFcrIiwgIiAiLCBteV9kYXRhX2hiZCRib2R5X3RleHQpCmBgYAoKCmBgYHtyfQojIFRva2VuaXNlIHRoZSBkYXRhIGFuZCByZW1vdmUgcHVuY3R1YXRpb24sIG51bWJlcnMsIGFuZCBzeW1ib2xzCiMgQ29udmVydCBhbGwgdGV4dCB0byBsb3dlcmNhc2UsIGFwcGx5IHN0ZW1taW5nLCBhbmQgcmVtb3ZlIG5vbi1pbmZvcm1hdGl2ZSB3b3JkcyBmb3IgdG9waWMgbW9kZWxsaW5nIHB1cnBvc2VzCnRtX3Rva2VucyA8LSB0b2tlbnMobXlfZGF0YV9oYmQkYm9keV90ZXh0LCByZW1vdmVfcHVuY3QgPSBUUlVFLCByZW1vdmVfbnVtYmVycyA9IFRSVUUsIHJlbW92ZV9zeW1ib2xzID0gVFJVRSkgJT4lIHRva2Vuc19yZW1vdmUoc3RvcHdvcmRzKCJlbmdsaXNoIikpICU+JSB0b2tlbnNfdG9sb3dlcigpICU+JSB0b2tlbnNfd29yZHN0ZW0obGFuZ3VhZ2UgPSBxdWFudGVkYV9vcHRpb25zKCJsYW5ndWFnZV9zdGVtbWVyIikpICU+JSB0b2tlbnNfcmVtb3ZlKGMoInBvbGljIiwgIm9mZmljIiwgInBlb3BsIiwgInllYXIiLCAiZGF5IiwgIndpbGwiLCAiYWxzbyIsICJ0IiwgInMiLCAiY2FuIiwgInVrIiwgImJyaXRpc2giKSkKYGBgCgpgYGB7cn0KIyBDcmVhdGUgYSBEb2N1bWVudCBGZWF0dXJlIE1hdHJpeCAoZGZtKQpkZm1fdG0gPC0gZGZtKHRtX3Rva2VucykKCiMgVHJpbSB0aGUgZGZtIGZvciBmcmVxdWVudGx5IGFuZCByYXJlbHkgb2NjdXJyaW5nIHRlcm1zCmRmbS50cmltIDwtIGRmbV90cmltKGRmbV90bSwgbWluX2RvY2ZyZXEgPSAwLjA3NSwgbWF4X2RvY2ZyZXEgPSAwLjksIGRvY2ZyZXFfdHlwZSA9ICJwcm9wIikKZGZtLnRyaW0KYGBgCgpgYGB7cn0KIyMgTERBIHRvcGljIG1vZGVsbGluZwpuLnRvcGljcyA8LSA1CmRmbTJ0b3BpY21vZGVscyA8LSBjb252ZXJ0KGRmbS50cmltLCB0byA9ICJ0b3BpY21vZGVscyIpCmxkYS5tb2RlbCA8LSBMREEoZGZtMnRvcGljbW9kZWxzLCBuLnRvcGljcywgY29udHJvbCA9IGxpc3Qoc2VlZD0xMTEpKQpsZGEubW9kZWwKYGBgCgpgYGB7cn0KIyBFeHRyYWN0IHRoZSB0b3BpYy10ZXJtIG1hdHJpeCAoYmV0YSkgZnJvbSB0aGUgTERBIG1vZGVsCmJldGFfdG9waWNzIDwtIHRpZHkobGRhLm1vZGVsLCBtYXRyaXggPSAiYmV0YSIpCmJldGFfdG9waWNzCmBgYAoKYGBge3J9CiMgR3JvdXAgdGhlIHRlcm1zIGJ5IHRvcGljcwpiZXRhX3RvcF90ZXJtcyA8LSBiZXRhX3RvcGljcyAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgc2xpY2VfbWF4KGJldGEsIG4gPSAxMCkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UodG9waWMsIC1iZXRhKQpgYGAKCmBgYHtyfQojIERpc3BsYXkgdGhlIGdyb3VwIHRlcm1zIApiZXRhX3RvcF90ZXJtcyAlPiUKICBtdXRhdGUodGVybSA9IHJlb3JkZXJfd2l0aGluKHRlcm0sIGJldGEsIHRvcGljKSkgJT4lCiAgZ2dwbG90KGFlcyhiZXRhLCB0ZXJtLCBmaWxsID0gZmFjdG9yKHRvcGljKSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsKICBzY2FsZV95X3Jlb3JkZXJlZCgpCmBgYAoKYGBge3J9CiMgVG9wIDEwIHRlcm1zIGZvciBlYWNoIHRvcGljcyAKYXMuZGF0YS5mcmFtZSh0ZXJtcyhsZGEubW9kZWwsIDEwKSkKCiMgTGlzdCB0aGUgdG9waWMgbnVtYmVyIGZvciBlYWNoIG5ld3MgYXJ0aWNsZSBpbiB0aGUgY29ycHVzLgpkYXRhLmZyYW1lKFRvcGljID0gdG9waWNzKGxkYS5tb2RlbCkpCmBgYAoKYGBge3J9CiMgQ29udmVydCB0aGUgdG9waWMgbW9kZWwgdG8gYSBkYXRhIGZyYW1lCnRvcGljc19kZiA8LSBkYXRhLmZyYW1lKHRvcGljcyhsZGEubW9kZWwpKQoKIyBQbG90IHRoZSBjb3VudCBvZiB0b3BpY3MKZ2dwbG90KHRvcGljc19kZiwgYWVzKHggPSBmYWN0b3IodG9waWNzKGxkYS5tb2RlbCkpKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsID0gZmFjdG9yKHRvcGljcyhsZGEubW9kZWwpKSksIGFscGhhID0gMC44KSArCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlRvcGljIikgKwogIGxhYnModGl0bGUgPSAiVG9waWMgRGlzdHJpYnV0aW9uIEJhc2VkIG9uIExhdGVudCBEaXJpY2hsZXQgQWxsb2NhdGlvbiAoTERBKSIsIHggPSAiVG9waWMiLCB5ID0gIkNvdW50IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImdyYXk5MCIpKSArCiAgZ2VvbV90ZXh0KHN0YXQ9J2NvdW50JywgYWVzKGxhYmVsPS4uY291bnQuLiksIHZqdXN0PS0wLjUpCgpgYGAKCgoKYGBge3J9CiMjIFN0cnVjdHVyYWwgVG9waWMgTW9kZWxpbmcgKFNUTSkKbi50b3BpY3MgPC0gNQpkZm0yc3RtIDwtIGNvbnZlcnQoZGZtLnRyaW0sIHRvID0gInN0bSIpCm1vZGVsbC5zdG0gPC0gc3RtKGRmbTJzdG0kZG9jdW1lbnRzLCBkZm0yc3RtJHZvY2FiLCBLID0gbi50b3BpY3MsIGRhdGEgPSBkZm0yc3RtJG1ldGEsIGluaXQudHlwZSA9ICJTcGVjdHJhbCIpCmBgYAoKYGBge3J9CiMgVG9wIDEwIHRlcm1zIGZvciBlYWNoIHRvcGljCmFzLmRhdGEuZnJhbWUodChsYWJlbFRvcGljcyhtb2RlbGwuc3RtLCBuID0gMTApJHByb2IpKQoKIyBHZW5lcmF0ZSBhIHdvcmQgY2xvdWQgZm9yIHRoZSBmaXJzdCB0b3BpYwpwYXIobWFyPWMoMC41LCAwLjUsIDAuNSwgMC41KSkKY2xvdWQobW9kZWxsLnN0bSwgdG9waWMgPSAxLCBzY2FsZSA9IGMoMi4yNSwuNSkpCgojIFBsb3QgYSBzdW1tYXJ5IG9mIHRoZSBlc3RpbWF0ZWQgdG9waWMgc2hhcmVzIGZvciB0aGUgY29ycHVzCnBsb3QobW9kZWxsLnN0bSwgdHlwZSA9ICJzdW1tYXJ5IiwgdGV4dC5jZXggPSAwLjUsIG1haW4gPSAiVG9waWMgc2hhcmVzIG9uIHRoZSBjb3JwdXMgYXMgYSB3aG9sZSIsIHhsYWIgPSAiZXN0aW1hdGVkIHNoYXJlIG9mIHRvcGljcyIpCmBgYAoKCmBgYHtyfQojIFByaW50IHRoZSB0b3AgNSB0ZXJtcyBmb3IgZWFjaCBvZiB0aGUgNSB0b3BpY3MKbGFiZWxUb3BpY3MobW9kZWxsLnN0bSx0b3BpY3MgPSBjKDE6NSksIG49MTApCgojIFBsb3QgaGlzdG9ncmFtcyBvZiB0aGUgZXN0aW1hdGVkIHRvcGljIHNoYXJlcyB3aXRoaW4gdGhlIGRvY3VtZW50cyBmb3IgMyByYW5kb21seSBzZWxlY3RlZCB0b3BpY3MKcGxvdChtb2RlbGwuc3RtLCB0eXBlID0gImhpc3QiLCB0b3BpY3MgPSBzYW1wbGUoMTpuLnRvcGljcywgc2l6ZSA9IDMpLCBtYWluID0gImhpc3RvZ3JhbSBvZiB0aGUgdG9waWMgc2hhcmVzIHdpdGhpbiB0aGUgZG9jdW1lbnRzIikKCiMgUGxvdCB0aGUgdG9wIHRlcm1zIGZvciBlYWNoIG9mIHRoZSA1IHRvcGljcwpwbG90KG1vZGVsbC5zdG0sIHR5cGUgPSAibGFiZWxzIiwgdG9waWNzID0gYygxLDIsMyw0LDUpLCBtYWluID0gIlRvcGljIHRlcm1zIikKYGBgCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQojIElkZW50aWZ5IHRoZSBtb3N0IHByb2JhYmxlIHRvcGljIGZvciBlYWNoIGRvY3VtZW50CnRvcF90b3BpY3MgPC0gYXBwbHkobW9kZWxsLnN0bSR0aGV0YSwgMSwgd2hpY2gubWF4KQojIEFkZCB0aGUgdG9waWMgbGFiZWxzIHRvIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lCm15X2RhdGFfaGJkJFRvcGljX3N0bSA8LSB0b3BfdG9waWNzCiMgQ2FsY3VsYXRlIHRoZSBjb3VudCBvZiBlYWNoIHRvcGljCmRmX3RvcGljX2NvdW50cyA8LSBteV9kYXRhX2hiZCAlPiUKICBncm91cF9ieShUb3BpY19zdG0pICU+JQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSkKCiMgQ3JlYXRlIHRoZSBwbG90CmdncGxvdChkZl90b3BpY19jb3VudHMsIGFlcyh4ID0gZmFjdG9yKFRvcGljX3N0bSksIHkgPSBjb3VudCwgZmlsbCA9IGZhY3RvcihUb3BpY19zdG0pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuOCkgKwogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJUb3BpYyIpICsKICBsYWJzKHRpdGxlID0gIlRvcGljIERpc3RyaWJ1dGlvbiBCYXNlZCBvbiBTdHJ1Y3R1cmFsIFRvcGljIE1vZGVsaW5nIChTVE0pIiwgeCA9ICJUb3BpYyIsIHkgPSAiQ291bnQiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiZ3JheTkwIikpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY291bnQpLCB2anVzdD0tMC41KQoKYGBgCgoKCmBgYHtyfQojIyBUbyBzZWUgaG93IHRoZSB0b3BpY3MgY2hhbmdlZCBvdmVyIHRpbWUgKExEQSBtb2RlbCkKCiMgQWRkIHRoZSB0b3BpYyBsYWJlbHMgdG8gdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUKbXlfZGF0YV9oYmQkVG9waWNfbGRhIDwtIHRvcGljcyhsZGEubW9kZWwpCgojIENvbnZlcnQgdGhlIGRhdGUgY29sdW1uIHRvIGEgRGF0ZSBvYmplY3QKbXlfZGF0YV9oYmQkZmlyc3RfcHVibGljYXRpb25fZGF0ZSA8LSBhcy5EYXRlKG15X2RhdGFfaGJkJGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUsIGZvcm1hdCA9ICIlWS0lbS0lZCIpCgojIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIHRvcGljIGJ5IHllYXIKdG9waWNfcHJvcG9ydGlvbnMgPC0gbXlfZGF0YV9oYmQgJT4lCiAgZ3JvdXBfYnkoVG9waWNfbGRhLCB5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUpKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBtdXRhdGUocHJvcG9ydGlvbiA9IGNvdW50IC8gc3VtKGNvdW50KSkKCiMgUGxvdCB0aGUgdG9waWMgcHJvcG9ydGlvbnMgb3ZlciB0aW1lCmdncGxvdCh0b3BpY19wcm9wb3J0aW9ucywgYWVzKHggPSB5ZWFyLCB5ID0gcHJvcG9ydGlvbiwgY29sb3IgPSBmYWN0b3IoVG9waWNfbGRhKSkpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIlByb3BvcnRpb24iLCBjb2xvciA9ICJUb3BpYyIpICsKICB0aGVtZV9idygpCgojIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIHRvcGljIGJ5IGhhbGYteWVhcgp0b3BpY19wcm9wb3J0aW9uc19oYWxmX3llYXIgPC0gbXlfZGF0YV9oYmQgJT4lCiAgZ3JvdXBfYnkoVG9waWNfbGRhLCB5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUpLCBoYWxmX3llYXIgPSBsdWJyaWRhdGU6OmZsb29yX2RhdGUoZmlyc3RfcHVibGljYXRpb25fZGF0ZSwgdW5pdCA9ICI2IG1vbnRocyIpKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieSh5ZWFyLCBoYWxmX3llYXIpICU+JQogIG11dGF0ZShwcm9wb3J0aW9uID0gY291bnQgLyBzdW0oY291bnQpKQoKIyBQbG90IHRoZSB0b3BpYyBwcm9wb3J0aW9ucyBvdmVyIHRpbWUKZ2dwbG90KHRvcGljX3Byb3BvcnRpb25zX2hhbGZfeWVhciwgYWVzKHggPSBoYWxmX3llYXIsIHkgPSBwcm9wb3J0aW9uLCBjb2xvciA9IGZhY3RvcihUb3BpY19sZGEpKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHggPSAiSGFsZi1ZZWFyIiwgeSA9ICJQcm9wb3J0aW9uIiwgY29sb3IgPSAiVG9waWMiKSArCiAgdGhlbWVfYncoKQpgYGAKCgoKYGBge3J9CiMjIFRvIHNlZSBob3cgdGhlIHRvcGljcyBjaGFuZ2VkIG92ZXIgdGltZSAoU1RNIG1vZGVsKQoKIyBJZGVudGlmeSB0aGUgbW9zdCBwcm9iYWJsZSB0b3BpYyBmb3IgZWFjaCBkb2N1bWVudAp0b3BfdG9waWNzIDwtIGFwcGx5KG1vZGVsbC5zdG0kdGhldGEsIDEsIHdoaWNoLm1heCkKIyBBZGQgdGhlIHRvcGljIGxhYmVscyB0byB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZQpteV9kYXRhX2hiZCRUb3BpY19zdG0gPC0gdG9wX3RvcGljcwoKIyBDYWxjdWxhdGUgdGhlIHByb3BvcnRpb24gb2YgZWFjaCB0b3BpYyBieSB5ZWFyCnRvcGljX3Byb3BvcnRpb25zX3N0bSA8LSBteV9kYXRhX2hiZCAlPiUKICBncm91cF9ieShUb3BpY19zdG0sIHllYXIgPSBsdWJyaWRhdGU6OnllYXIoZmlyc3RfcHVibGljYXRpb25fZGF0ZSkpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgdW5ncm91cCgpICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIG11dGF0ZShwcm9wb3J0aW9uID0gY291bnQgLyBzdW0oY291bnQpKQoKIyBQbG90IHRoZSB0b3BpYyBwcm9wb3J0aW9ucyBvdmVyIHRpbWUKZ2dwbG90KHRvcGljX3Byb3BvcnRpb25zX3N0bSwgYWVzKHggPSB5ZWFyLCB5ID0gcHJvcG9ydGlvbiwgY29sb3IgPSBmYWN0b3IoVG9waWNfc3RtKSkpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIlByb3BvcnRpb24iLCBjb2xvciA9ICJUb3BpYyIpICsKICB0aGVtZV9idygpCgojIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIHRvcGljIGJ5IGhhbGYteWVhcgp0b3BpY19wcm9wb3J0aW9uc19zdG1faGFsZl95ZWFyIDwtIG15X2RhdGFfaGJkICU+JQogIGdyb3VwX2J5KFRvcGljX3N0bSwgeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihmaXJzdF9wdWJsaWNhdGlvbl9kYXRlKSwgaGFsZl95ZWFyID0gbHVicmlkYXRlOjpmbG9vcl9kYXRlKGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUsIHVuaXQgPSAiNiBtb250aHMiKSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoeWVhciwgaGFsZl95ZWFyKSAlPiUKICBtdXRhdGUocHJvcG9ydGlvbiA9IGNvdW50IC8gc3VtKGNvdW50KSkKCgojIFBsb3QgdGhlIHRvcGljIHByb3BvcnRpb25zIG92ZXIgdGltZQpnZ3Bsb3QodG9waWNfcHJvcG9ydGlvbnNfc3RtX2hhbGZfeWVhciwgYWVzKHggPSBoYWxmX3llYXIsIHkgPSBwcm9wb3J0aW9uLCBjb2xvciA9IGZhY3RvcihUb3BpY19zdG0pKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHggPSAiSGFsZi1ZZWFyIiwgeSA9ICJQcm9wb3J0aW9uIiwgY29sb3IgPSAiVG9waWMiKSArCiAgdGhlbWVfYncoKQpgYGAKCmBgYHtyfQojIFJlZmluZSB0aGUgZ3JhcGhzLCBhZGQgdGhlIHRvcCA1IHRlcm1zIGZvciBlYWNoIHRvcGljIHRvIHRoZSBsZWdlbmQgKExEQSBtb2RlbCkKCiMgRXh0cmFjdCB0aGUgdG9wIDUgdGVybXMgZm9yIGVhY2ggdG9waWMKdG9wX3Rlcm1zIDwtIHRlcm1zKGxkYS5tb2RlbCwgNSkKCiMgQ3JlYXRlIGxhYmVsIGZvciBjb2xvciBsZWdlbmQKbGVnZW5kX2xhYmVsIDwtIHBhc3RlKCJUb3AgNSBUZXJtc1xuIiwKICAgICAgICAgICAgICAgICAgICAgICIxOiAiLCBwYXN0ZSh0b3BfdGVybXNbLDFdLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iLAogICAgICAgICAgICAgICAgICAgICAgIjI6ICIsIHBhc3RlKHRvcF90ZXJtc1ssMl0sIGNvbGxhcHNlID0gIiwgIiksICJcbiIsCiAgICAgICAgICAgICAgICAgICAgICAiMzogIiwgcGFzdGUodG9wX3Rlcm1zWywzXSwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIiwKICAgICAgICAgICAgICAgICAgICAgICI0OiAiLCBwYXN0ZSh0b3BfdGVybXNbLDRdLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iLAogICAgICAgICAgICAgICAgICAgICAgIjU6ICIsIHBhc3RlKHRvcF90ZXJtc1ssNV0sIGNvbGxhcHNlID0gIiwgIiksIHNlcCA9ICIiKQoKIyBQbG90IHRoZSB0b3BpYyBwcm9wb3J0aW9ucyBvdmVyIHRpbWUKZ2dwbG90KHRvcGljX3Byb3BvcnRpb25zX2hhbGZfeWVhciwgYWVzKHggPSBoYWxmX3llYXIsIHkgPSBwcm9wb3J0aW9uLCBjb2xvciA9IGZhY3RvcihUb3BpY19sZGEpKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHggPSAiSGFsZi1ZZWFyIiwgeSA9ICJQcm9wb3J0aW9uIiwgCiAgICAgICBjb2xvciA9IGxlZ2VuZF9sYWJlbCwKICAgICAgIHN1YnRpdGxlID0gIlRvcGljIENoYW5nZXMgT3ZlciBUaW1lIFVzaW5nIExEQSBUb3BpYyBNb2RlbGxpbmciKSArCiAgdGhlbWVfYncoKQoKYGBgCgoKYGBge3J9CiMgUmVmaW5lIHRoZSBncmFwaHMsIGFkZCB0aGUgdG9wIDUgdGVybXMgZm9yIGVhY2ggdG9waWMgdG8gdGhlIGxlZ2VuZCAoU1RNIG1vZGVsKQoKIyBFeHRyYWN0IHRoZSB0b3AgNSB0ZXJtcyBmb3IgZWFjaCB0b3BpYwp0b3BfdGVybXNfc3RtIDwtIGxhYmVsVG9waWNzKG1vZGVsbC5zdG0sIHRvcGljcyA9IGMoMTo1KSwgbj01KQp0b3BfdGVybXNfc3RtJGZyZXgKCiMgQ3JlYXRlIGxhYmVsIGZvciBjb2xvciBsZWdlbmQKbGVnZW5kX2xhYmVsX3N0bSA8LSBwYXN0ZSgiVG9wIDUgVGVybXNcbiIsCiAgICAgICAgICAgICAgICAgICAgICAiMTogIiwgcGFzdGUodG9wX3Rlcm1zX3N0bSRmcmV4WzEsXSwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIiwKICAgICAgICAgICAgICAgICAgICAgICIyOiAiLCBwYXN0ZSh0b3BfdGVybXNfc3RtJGZyZXhbMixdLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iLAogICAgICAgICAgICAgICAgICAgICAgIjM6ICIsIHBhc3RlKHRvcF90ZXJtc19zdG0kZnJleFszLF0sIGNvbGxhcHNlID0gIiwgIiksICJcbiIsCiAgICAgICAgICAgICAgICAgICAgICAiNDogIiwgcGFzdGUodG9wX3Rlcm1zX3N0bSRmcmV4WzQsXSwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIiwKICAgICAgICAgICAgICAgICAgICAgICI1OiAiLCBwYXN0ZSh0b3BfdGVybXNfc3RtJGZyZXhbNSxdLCBjb2xsYXBzZSA9ICIsICIpLCBzZXAgPSAiIikKCiMgUGxvdCB0aGUgdG9waWMgcHJvcG9ydGlvbnMgb3ZlciB0aW1lCmdncGxvdCh0b3BpY19wcm9wb3J0aW9uc19zdG1faGFsZl95ZWFyLCBhZXMoeCA9IGhhbGZfeWVhciwgeSA9IHByb3BvcnRpb24sIGNvbG9yID0gZmFjdG9yKFRvcGljX3N0bSkpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnMoeCA9ICJIYWxmLVllYXIiLCB5ID0gIlByb3BvcnRpb24iLCAKICAgICAgIGNvbG9yID0gbGVnZW5kX2xhYmVsX3N0bSwKICAgICAgIHN1YnRpdGxlID0gIlRvcGljIENoYW5nZXMgT3ZlciBUaW1lIFVzaW5nIFN0cnVjdHVyYWwgVG9waWMgTW9kZWxpbmcgKFNUTSkiKSArCiAgdGhlbWVfYncoKQoKYGBgCgpgYGB7cn0KIyBUcnkgdG8gY29tYmluZSBzZW50aW1lbnQgYW5kIHRvcGljIGNoYW5nZXMgb3ZlciB0aW1lCmxpYnJhcnkocGF0Y2h3b3JrKQoKIyBDcmVhdGUgYSBsaW5lIGNoYXJ0IG9mIHNlbnRpbWVudCBzY29yZSBvdmVyIHRpbWUKcDEgPC0gZ2dwbG90KHNlbnRpbWVudF9ieV9kYXRlLCBhZXMoeCA9IGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUsIHkgPSBzZW50aW1lbnRfc2NvcmUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKSArCiAgbGFicyh0aXRsZSA9ICJTZW50aW1lbnQgU2NvcmUgb3ZlciBUaW1lIEJhc2VkIG9uIFN5dXpoZXQgUGFja2FnZSIsIHggPSAiRGF0ZSIsIHkgPSAiU2VudGltZW50IFNjb3JlIikgKwogIHRoZW1lX21pbmltYWwoKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSkpCgojIFBsb3QgdGhlIHRvcGljIHByb3BvcnRpb25zIG92ZXIgdGltZSB1c2luZyBMREEKcDIgPC0gZ2dwbG90KHRvcGljX3Byb3BvcnRpb25zX2hhbGZfeWVhciwgYWVzKHggPSBoYWxmX3llYXIsIHkgPSBwcm9wb3J0aW9uLCBjb2xvciA9IGZhY3RvcihUb3BpY19sZGEpKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHggPSAiSGFsZi1ZZWFyIiwgeSA9ICJQcm9wb3J0aW9uIiwgCiAgICAgICBjb2xvciA9ICJUb3BpYyIsIHN1YnRpdGxlID0gIlRvcGljIENoYW5nZXMgT3ZlciBUaW1lIFVzaW5nIExEQSBUb3BpYyBNb2RlbGxpbmciKSArCiAgdGhlbWVfYncoKQoKIyBQbG90IHRoZSB0b3BpYyBwcm9wb3J0aW9ucyBvdmVyIHRpbWUgdXNpbmcgU1RNCnAzIDwtIGdncGxvdCh0b3BpY19wcm9wb3J0aW9uc19zdG1faGFsZl95ZWFyLCBhZXMoeCA9IGhhbGZfeWVhciwgeSA9IHByb3BvcnRpb24sIGNvbG9yID0gZmFjdG9yKFRvcGljX3N0bSkpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnMoeCA9ICJIYWxmLVllYXIiLCB5ID0gIlByb3BvcnRpb24iLCAKICAgICAgIGNvbG9yID0gIlRvcGljIiwKICAgICAgIHN1YnRpdGxlID0gIlRvcGljIENoYW5nZXMgT3ZlciBUaW1lIFVzaW5nIFN0cnVjdHVyYWwgVG9waWMgTW9kZWxpbmciKSArCiAgdGhlbWVfYncoKQoKIyBDb21iaW5lIHRoZSB0aHJlZSBwbG90cyB2ZXJ0aWNhbGx5CnAxICsgcDIgKyBwMyArIHBsb3RfbGF5b3V0KG5jb2wgPSAxKQoKYGBgCgoKCgoKCgo=